package com.gcsoftware.gateway.redis;

import com.gcsoftware.gateway.confLoader.LoadGWRbacParam;
import org.apache.commons.lang3.SerializationUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.exceptions.JedisConnectionException;
import redis.clients.jedis.exceptions.JedisDataException;
import redis.clients.jedis.exceptions.JedisException;

import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.util.Iterator;
import java.util.Set;

/**
 * Created by qs on 2018/4/11.
 */
public class RedisUtils {
    private Jedis jedis;
    private static JedisPool jedisPool;
    private static boolean broken = false;
    private static Logger logger = LoggerFactory.getLogger(RedisUtils.class);

    public final String TOKEN_ROLE_PREFIX = "TOKEN_FOR_ROLE_";

    public final String ROLE_ACCESS_ENDPOINTS_PREFIX = "ACCESS_ENDPOINTS_";

    public static JedisPool getJedisPool() {
        broken = false;
        if (null == RedisUtils.jedisPool) {
            synchronized (RedisUtils.class) {
                logger.info("Start to Init Jedis Pool.");
                if (null == RedisUtils.jedisPool) {

                    LoadGWRbacParam rbacParam = LoadGWRbacParam.getInstance();
//                    RedisUtils.jedis = new Jedis(rbacParam.getRedisHost(),rbacParam.getRedisPort());
                    //1.设置连接池的配置对象
                    JedisPoolConfig config = new JedisPoolConfig();
                    //设置池中最大连接数 -1表示不限制
                    config.setMaxTotal(-1);
                    //设置空闲时池中保有的最大连接数
                    config.setMaxIdle(10);
                    config.setMaxWaitMillis(1000 * 100);
                    config.setTestOnBorrow(true);
                    //2.设置连接池对象
                    jedisPool = new JedisPool(config,rbacParam.getRedisHost(),rbacParam.getRedisPort());
                    //3.从池中获取连接对象
                   // RedisUtils.jedis = pool.getResource();
                    logger.debug("Init jedis pool.host=[{}],port=[{}]",rbacParam.getRedisHost(),rbacParam.getRedisPort());
                }
            }
        }
        return jedisPool;
    }

    public void setString(String key , String value) {
        try {
            logger.debug("Set String key-value 2 redis. key = [{}]" , key);
            jedis = getJedisPool().getResource();
            jedis.set(key, value);
        } catch (JedisException e) {
            logger.error("Set String key-value 2 redis failed. key = [{}], and will return this jedis 2 pool!" , key);
            broken = handleJedisException(e);
        } finally {
            closeResource(jedis,broken);
        }
    }

    public String getString(String key) {
        String retValue = "";
        try {
            logger.debug("Get String key-value from redis. key = [{}]" , key);
            jedis = getJedisPool().getResource();
            retValue = jedis.get(key);
            logger.debug("Get String key-value from redis , key=[{}] , value=[{}]" , key, retValue);
        } catch (JedisException e) {
            logger.error("Get String key-value from redis failed. key = [{}], and will return this jedis 2 pool!" , key);
            broken = handleJedisException(e);
        } finally {
            closeResource(jedis,broken);
            return retValue;
        }

    }


    public void del(String key) {
        try {
            logger.debug("Del from redis. key = [{}]" , key);
            jedis = getJedisPool().getResource();
            jedis.del(key);
        } catch (JedisException e) {
            logger.error("Del from redis failed. key = [{}], and will return this jedis 2 pool!" , key);
            broken = handleJedisException(e);
        } finally {
            closeResource(jedis,broken);
        }
    }

    /**
     * Handle jedisException, write log and return whether the connection is broken.
     */
    protected boolean handleJedisException(JedisException jedisException) {
        if (jedisException instanceof JedisConnectionException) {
            logger.error("Redis connection " + LoadGWRbacParam.getInstance().getRedisHost() + ":" + LoadGWRbacParam.getInstance().getRedisPort()  + " lost.", jedisException);
        } else if (jedisException instanceof JedisDataException) {
            if ((jedisException.getMessage() != null) && (jedisException.getMessage().indexOf("READONLY") != -1)) {
                logger.error("Redis connection " + LoadGWRbacParam.getInstance().getRedisHost() + ":" + LoadGWRbacParam.getInstance().getRedisPort()  + " are read-only slave.", jedisException);
            } else {
                // dataException, isBroken=false
                return false;
            }
        } else {
            logger.error("Jedis exception happen.", jedisException);
        }
        return true;
    }

    /**
     * Return jedis connection to the pool, call different return methods depends on the conectionBroken status.
     */
    protected void closeResource(Jedis jedis, boolean conectionBroken) {
        try {
            if (conectionBroken) {
                getJedisPool().returnBrokenResource(jedis);
            } else {
                getJedisPool().returnResource(jedis);
            }
            logger.debug("Return back jedis successed.");
        } catch (Exception e) {
            logger.error("return back jedis failed, will fore close the jedis.", e);
            //JedisUtils.destroyJedis(jedis);
//            jedisPool.returnResource();
        }
    }

}
